﻿#include <QCoreApplication>
#include <QMutex>
#include <QDateTime>
#include <QFile>
#include <QTextStream>
#include <QQmlEngine>

#include "qlogger.h"

QLogger::MsgTypes QLogger::m_msgTypes = MsgType::AllMsg;
QLogger::MsgContextVisible QLogger::m_msgContextVisible = MsgContextVisible::Auto;
QString QLogger::m_logFileFullPath = "log.txt";

/*!
    \class QLogger
    \inherits QObject
    \brief The QLogger class provides the ability to output logs in
    C++ and QML environments.

    Typical uses of QLogger are as follows:

    \code
    //main.cpp
    #include "qlogger.h"
    int main(){
        //....

        QLogger::init();
        QLogger::setMsgContextVisible(QLogger::Auto);   //Sets whether to output the file name and line number
        QLogger::setMsgTypes(QLogger::AllMsg);          //Sets the type of message that can be output
        QLogger::setLogFileFullPath("./logfile.txt")    //Sets the storage path for the log file

        qDebug()<<"C++ Debug Message";
        qWarning()<<"C++ Warning Message";
        qCritical()<<"C++ Critical Message";
        qInfo()<<"C++ Info Message";

        //...
    }

    //main.qml
    import QtConfig 1.0
    Window{
        Component.onCompleted: {
            console.log("Qml Debug Message")
            console.warn("Qml Warning Message")
            console.error("Qml Critical Message")
            console.info("Qml Info Message")
        }
    }
    \endcode
*/


/*!
    Load the logger.
*/
void QLogger::load()
{
    qInstallMessageHandler(defaultMsgHandler);
}

/*!
    Unload the logger
*/
void QLogger::unload()
{
    qInstallMessageHandler(0);
}

/*!
    Returns the current messge context visibility.The defualt value is
    MsgContextVisible::Auto.

    \note messge context contains the name and line number of the file
    in which the log message is outputted.
*/
QLogger::MsgContextVisible QLogger::msgContextVisible()
{
    return m_msgContextVisible;
}

/*!
    Sets the current messge context visibility to \a visible.
*/
void QLogger::setMsgContextVisible(QLogger::MsgContextVisible visible)
{
    m_msgContextVisible = visible;
}

/*!
    Returns the current output message type. The default value is
    MsgType::AllMsg.
*/
QLogger::MsgTypes QLogger::msgTypes()
{
    return m_msgTypes;
}

/*!
    Sets the current output message type to \a typess. You can combine
    multiple message types such as:
    \code
    setMsgTypes(QLogger::DebugMsg | QLogger::WarningMsg)
    \endcode
*/
void QLogger::setMsgTypes(MsgTypes types)
{
    m_msgTypes = types;
}

/*!
    Returns the current log file output path including file name.
*/
QString QLogger::logFileFullPath()
{
    return m_logFileFullPath;
}

/*!
    Sets the current log file output path to \a path.
*/
void QLogger::setLogFileFullPath(QString path)
{
    m_logFileFullPath = path;
}

/*!
    \internal
    Constructs object.
*/
QLogger::QLogger(QObject *parent):QObject(parent)
{

}

/*!
    \internal
    This function is QtMessageHandler will be passed to qInstallMessageHandler().
*/
void QLogger::defaultMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{

    QLogger::MsgType msgType;
    QString messageType;
    switch(type)
    {
    case QtMsgType::QtDebugMsg:
        messageType = QString("[Debug]");
        msgType = MsgType::DebugMsg;
        break;
    case QtMsgType::QtWarningMsg:
        messageType = QString("[Warning]");
        msgType = MsgType::WarningMsg;
        break;
    case QtMsgType::QtCriticalMsg:
        messageType = QString("[Critical]");
        msgType = MsgType::CriticalMsg;
        break;
    case QtMsgType::QtFatalMsg:
        messageType = QString("[Fatal]");
        msgType = MsgType::FatalMsg;
    case QtMsgType::QtInfoMsg:
        messageType = QString("[Info]");
        msgType = MsgType::InfoMsg;
        break;
    }

    //Check the MsgType flag to determine whether to output it
    if(!m_msgTypes.testFlag(MsgType::AllMsg)){
        if(!m_msgTypes.testFlag(msgType)){
            return;
        }
    }

    static QMutex mutex;
    mutex.lock();

    QString current_date = QString("[%1]").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
    QString context_info = QString("[%1:%2]").arg(QString(context.file)).arg(context.line);

    // Check the MsgContextVisible enum to determine whether to output context message.
    // Note if set MsgContextVisible to AlwaysShow, and build in release mode, you must
    // add DEFINES += QT_MESSAGELOGCONTEXT in .pro file.
    QString message;
    if(m_msgContextVisible==MsgContextVisible::Auto){
#ifdef QT_NO_DEBUG
    message = QString("%1 %2 - %3").arg(current_date).arg(messageType).arg(msg);
#else
    message = QString("%1 %2 %3 - %4").arg(current_date).arg(messageType).arg(context_info).arg(msg);
#endif
    }else if(m_msgContextVisible==MsgContextVisible::AlwaysShow){
        message = QString("%1 %2 %3 - %4").arg(current_date).arg(messageType).arg(context_info).arg(msg);
    }else if(m_msgContextVisible==MsgContextVisible::AlwaysClose){
        message = QString("%1 %2 - %3").arg(current_date).arg(messageType).arg(msg);
    }
    QFile file(m_logFileFullPath);
    file.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream text_stream(&file);
    text_stream << message << "\r\n";
    file.flush();
    file.close();

    mutex.unlock();
}
